﻿using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Drawing;
using Microsoft.Win32;
using System.Diagnostics;
using System.Windows.Forms;
using Microsoft.VisualBasic.FileIO;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Threading;

namespace System.Custom
{
   public enum OS
   {
      Vista,
      Windows7,
      Windows98,
      WindowsXP,
      WindowsNT4,
      Windows2000,
      VistaOrLater,
      Other
   }

   public static partial class Win32
   {
      public enum SystemCommand
      {
         SC_SIZE = 0xF000,
         SC_MOVE = 0xF010,
         SC_MINIMIZE = 0xF020,
         ///<summary>
         /// Sent when form maximizes
         ///</summary>
         SC_MAXIMIZE = 0xF030,
         ///<summary>
         /// Sent when form maximizes because of doubcle click on caption
         ///</summary>
         SC_MAXIMIZE2 = 0xF032,
         SC_NEXTWINDOW = 0xF040,
         SC_PREVWINDOW = 0xF050,
         ///<summary>
         /// Closes the form
         ///</summary>
         SC_CLOSE = 0xF060,
         SC_VSCROLL = 0xF070,
         SC_HSCROLL = 0xF080,
         SC_MOUSEMENU = 0xF090,
         SC_KEYMENU = 0xF100,
         SC_ARRANGE = 0xF110,

         ///<summary>
         /// Sent when form is maximized from the taskbar
         ///</summary>
         SC_RESTORE = 0xF120,
         ///<summary>
         /// Sent when form maximizes because of doubcle click on caption
         ///</summary>
         SC_RESTORE2 = 0xF122,
         SC_TASKLIST = 0xF130,
         SC_SCREENSAVE = 0xF140,
         SC_HOTKEY = 0xF150,
         SC_DEFAULT = 0xF160,
         SC_MONITORPOWER = 0xF170,
         SC_CONTEXTHELP = 0xF180,
         SC_SEPARATOR = 0xF00F
      }
      
      public struct FlashWindowInfo
      {
         public int cbSize;
         public int uCount;
         public IntPtr hwnd;
         public int dwFlags;
         public int dwTimeout;
      }

      [StructLayout(LayoutKind.Sequential)]
      internal struct LASTINPUTINFO
      {
         public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO));
         [MarshalAs(UnmanagedType.U4)]
         public int cbSize;
         [MarshalAs(UnmanagedType.U4)]
         public UInt32 dwTime;
      }

      public static UInt16 LOWORD(IntPtr param)
      {
         return (UInt16)((UInt32)(param) & 0xffff);
      }
      public static UInt16 HIWORD(IntPtr param)
      {
         return (UInt16)((UInt32)(param) >> 16);
      }

      [DllImport("user32.dll")]
      public static extern bool DestroyIcon(IntPtr handle);

      [DllImport("user32.dll")]
      public static extern bool FlashWindow(IntPtr hwnd, bool invert);

      [DllImport("user32.dll")]
      public static extern bool FlashWindowEx(ref FlashWindowInfo fwi);

      [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
      public static extern bool ReleaseCapture();

      [DllImport("user32.dll")]
      public static extern bool SetMenuItemBitmaps(IntPtr hMenu, uint uPosition, uint uFlags, IntPtr hBitmapUnchecked, IntPtr hBitmapChecked);
      
      [DllImport("user32.dll")]
      internal static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

      [DllImport("Psapi.dll")]
      public static extern bool EmptyWorkingSet(IntPtr hProcess);

      [DllImport("Kernel32.dll")]
      public static extern IntPtr GetCurrentProcess();
      
      [DllImport("Kernel32.dll")]
      public static extern bool SetProcessWorkingSetSize(IntPtr hProcess, uint dwMinimumWorkingSetSize, uint dwMaximumWorkingSetSize);

      [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
      public static extern int GetShortPathName([MarshalAs(UnmanagedType.LPTStr)] string longPath,
                                                [MarshalAs(UnmanagedType.LPTStr)] StringBuilder shortPath,
                                                int shortPathLength);
   }

   public static partial class Shell
   {
      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
      public struct SHFILEOPSTRUCT
      {
         public IntPtr hwnd;
         [MarshalAs(UnmanagedType.U4)]
         public int wFunc;
         public string pFrom;
         public string pTo;
         public short fFlags;
         [MarshalAs(UnmanagedType.Bool)]
         public bool fAnyOperationsAborted;
         public IntPtr hNameMappings;
         public string lpszProgressTitle;
      }

      const int FO_MOVE = 1;
      const int FO_COPY = 2;
      const int FO_DELETE = 3;
      const int FO_RENAME = 4;
      const int FOF_ALLOWUNDO = 0x40;
      const int FOF_NOCONFIRMATION = 0x10;

      public static bool FileDialogVisible
      {
         get;
         internal set;
      }

      public static event EventHandler FileDialogVisibleChanged;

      static string GetSHFileOperationException(int errCode)
      {
         switch (errCode)
         {
            case 0x71: return "The source and destination files are the same file.";
            case 0x72: return "Multiple file paths were specified in the source buffer, but only one destination file path.";
            case 0x73: return "Rename operation was specified but the destination path is a different directory. Use the move operation instead.";
            case 0x74: return "The source is a root directory, which cannot be moved or renamed.";
            case 0x75: return "The operation was cancelled by the user, or silently cancelled if the appropriate flags were supplied to SHFileOperation.";
            case 0x76: return "The destination is a subtree of the source.";
            case 0x78: return "Security settings denied access to the source.";
            case 0x79: return "The source or destination path exceeded or would exceed MAX_PATH.";
            case 0x7A: return "The operation involved multiple destination paths, which can fail in the case of a move operation.";
            case 0x7C: return "The path in the source or destination or both was invalid.";
            case 0x7D: return "The source and destination have the same parent folder.";
            case 0x7E: return "The destination path is an existing file.";
            case 0x80: return "The destination path is an existing folder.";
            case 0x81: return "The name of the file exceeds MAX_PATH.";
            case 0x82: return "The destination is a read-only CD-ROM, possibly unformatted.";
            case 0x83: return "The destination is a read-only DVD, possibly unformatted.";
            case 0x84: return "The destination is a writable CD-ROM, possibly unformatted.";
            case 0x85: return "The file involved in the operation is too large for the destination media or file system.";
            case 0x86: return "The source is a read-only CD-ROM, possibly unformatted.";
            case 0x87: return "The source is a read-only DVD, possibly unformatted.";
            case 0x88: return "The source is a writable CD-ROM, possibly unformatted.";
            case 0xB7: return "MAX_PATH was exceeded during the operation.";
            case 0x402: return "An unknown error occurred. This is typically due to an invalid path in the source or destination. This error does not occur on Windows Vista and later.";
            case 0x10000: return "An unspecified error occurred on the destination.";
            case 0x10074: return "Destination is a root directory and cannot be renamed.";
            default: return "An unknown error occurred. Error code: " + errCode;
         }
      }

      [DllImport("shell32.dll", CharSet = CharSet.Auto)]
      static extern int SHFileOperation(ref SHFILEOPSTRUCT fileOp);

      public static string GetShortPath(string path)
      {
         StringBuilder shortPath = new StringBuilder(255);
         Win32.GetShortPathName(path, shortPath, shortPath.Capacity);
         return shortPath.ToString();
      }

      public static void MoveToTrash(string path)
      {
         SHFILEOPSTRUCT shf = new SHFILEOPSTRUCT();
         shf.wFunc = FO_DELETE;
         shf.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
         shf.pFrom = path + "\0";
         int r = SHFileOperation(ref shf);
         if (r != 0) throw new IOException(GetSHFileOperationException(r));
      }

      public static void Delete(string path)
      {
         SHFILEOPSTRUCT shf = new SHFILEOPSTRUCT();
         shf.wFunc = FO_DELETE;
         shf.fFlags = FOF_NOCONFIRMATION;
         shf.pFrom = path + "\0";
         int r = SHFileOperation(ref shf);
         if (r != 0) throw new IOException(GetSHFileOperationException(r));
      }

      public static void Copy(string from, string to, bool ui)
      {
         SHFILEOPSTRUCT shf = new SHFILEOPSTRUCT();
         shf.wFunc = FO_COPY;
         if (ui == false) shf.fFlags = FOF_NOCONFIRMATION;
         shf.pFrom = from.Trim('\\') + "\0";
         shf.pTo = to + "\0";
         int r = SHFileOperation(ref shf);
         if (r != 0) throw new IOException(GetSHFileOperationException(r));
      }

      public static void Move(string from, string to, bool ui)
      {
         SHFILEOPSTRUCT shf = new SHFILEOPSTRUCT();
         shf.wFunc = FO_MOVE;
         if (ui == false) shf.fFlags = FOF_NOCONFIRMATION;
         shf.pFrom = from + "\0";
         shf.pTo = to + "\0";
         int r = SHFileOperation(ref shf);
         if (r != 0) throw new IOException(GetSHFileOperationException(r));
      }

      public static bool OpenLink(string url)
      {
         return OpenLink(url, "");
      }

      public static bool OpenLink(string url, string browser)
      {
         if (browser == null) browser = "";

         browser = browser.ToLower();

         try
         {
            Process process = null;

            try
            {
               if (browser.Contains("opera"))
               {
                  browser = Registry.ClassesRoot.OpenSubKey(@"Applications\opera.exe\shell\open\command").GetValue("").ToString();
               }
               else if (browser.Contains("ie"))
               {
                  browser = Registry.ClassesRoot.OpenSubKey(@"Applications\iexplore.exe\shell\open\command").GetValue("").ToString();
               }
               else if (browser.Contains("chrome"))
               {
                  browser = Registry.ClassesRoot.OpenSubKey(@"Applications\chrome.exe\shell\open\command").GetValue("").ToString();
               }
               else if (browser.Contains("mozilla"))
               {
                  browser = Registry.ClassesRoot.OpenSubKey(@"FirefoxHTML\shell\open\command").GetValue("").ToString();
               }
               else
               {
                  browser = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").GetValue("Progid").ToString();
                  browser = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Classes\" + browser + @"\shell\open\command").GetValue("").ToString();
               }

               process = Process.Start(browser.SubstrBefore(".exe") + ".exe\"", url);
            }
            catch
            {
               process = Process.Start(url);
            }

            return process != null;
         }
         catch
         {
            return false;
         }
      }

      public static void CreateWebBrowser(string caption, string url)
      {
         Form wbForm = new Form();
         WebBrowser wb = new WebBrowser();

         wbForm.Controls.Add(wb);
         wbForm.ShowIcon = false;
         wb.Dock = DockStyle.Fill;
         wbForm.StartPosition = FormStartPosition.CenterScreen;
         wbForm.Size = new SizeF(Screen.PrimaryScreen.WorkingArea.Width * 0.8f,
                                 Screen.PrimaryScreen.WorkingArea.Height * 0.8f).ToSize();

         wb.DocumentTitleChanged += delegate(object s, EventArgs a)
         {
            wbForm.Text = wb.DocumentTitle;
         };
         wbForm.Disposed += delegate(object s, EventArgs a)
         {
            wb.Dispose();
         };

         wbForm.Show();
         wbForm.Text = caption;
         wb.Navigate(url, "");
      }
      
      public static Process RunProcess(string path, string args, bool shell)
      {
         try
         {
            Process p = new Process();

            p.StartInfo.FileName = path;
            p.StartInfo.Arguments = args;
            p.StartInfo.UseShellExecute = shell;

            p.Start();

            return p;
         }
         catch (Exception)
         {
            return null;
         }
      }

      public static Process RunProcess(string path, string args, bool hidden, out string output)
      {
         output = "";
         
         try
         {
            ProcessStartInfo si = new ProcessStartInfo();

            si.FileName = path;
            si.Arguments = args;
            si.CreateNoWindow = hidden;
            si.UseShellExecute = false;
            si.RedirectStandardError = true;
            si.RedirectStandardOutput = true;

            Process p = Process.Start(si);
            p.WaitForExit();

            output = p.StandardOutput.ReadToEnd() + Environment.NewLine;
            output += p.StandardError.ReadToEnd() + Environment.NewLine;

            return p;
         }
         catch (Exception error)
         {
            output = error.ToString();
            return null;
         }
      }

      public static Version GetAssemblyVersion(string path)
      {
         return File.Exists(path) ? new Version(FileVersionInfo.GetVersionInfo(path).FileVersion) : null;
      }

      public static int CompareVersions(string path1, string path2)
      {
         try
         {
            Version v1 = GetAssemblyVersion(path1);
            Version v2 = GetAssemblyVersion(path2);

            if (v1 != null)
            {
               return v1.CompareTo(v2);
            }
            else if (v2 != null)
            {
               return -1;
            }
            return 0;
         }
         catch (Exception)
         {
            return 0;
         }
      }

      public static List<string> OpenFileDialog(string title, string initDir, string filters)
      {
         if (string.IsNullOrEmpty(initDir) || !Directory.Exists(initDir))
         {
            try
            {
               RegistryKey rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\OpenFileDialog");

               initDir = Path.GetDirectoryName(rk.GetValue("", "").ToString().Split(';')[0]);

               rk.Close();
            }
            catch (Exception)
            {
               //
            }
         }

         ProcessStartInfo si = new ProcessStartInfo("rundll32.exe",
            GetShortPath(Application.StartupPath + "\\NativeUtilities.dll") + ",OpenFileDialog " +
            "0" + "?" + title + "?" + initDir + "?" + filters);

         si.UseShellExecute = false;

         Process p = Process.Start(si);

         if (p == null) throw new InvalidOperationException("Unable to open file dialog.");

         try
         {
            FileDialogVisible = true;
            if (FileDialogVisibleChanged != null) FileDialogVisibleChanged(p, EventArgs.Empty);
         }
         catch { }

         while (true)
         {
            if (p.HasExited)
            {
               try
               {
                  FileDialogVisible = false;
                  if (FileDialogVisibleChanged != null) FileDialogVisibleChanged(p, EventArgs.Empty);
               }
               catch { }

               break;
            }
            Application.DoEvents();
            Thread.Sleep(10);
         }

         string selection = "";

         try
         {
            RegistryKey rk = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\OpenFileDialog");

            selection = rk.GetValue("", "").ToString();

            rk.Close();
         }
         catch (Exception)
         {
            //
         }

         List<string> pathList = selection.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList();

         if (pathList.Count > 1)
         {
            for (int i = 1; i < pathList.Count; i++)
            {
               pathList[i] = pathList[0] + "\\" + pathList[i];
            }
            pathList.RemoveAt(0);
         }

         return pathList;
      }
   }

   public static class Computer
   {
      public static OS OSPlatform
      {
         get
         {
            if (Environment.OSVersion.Platform != PlatformID.Win32NT) return OS.Other;

            if (Environment.OSVersion.Version.Major == 5)
            {
               return OS.WindowsXP;
            }
            else if (Environment.OSVersion.Version.Major == 6)
            {
               return OS.VistaOrLater;
            }

            return OS.Other;
         }
      }

      public static string OSName
      {
         get
         {
            if (Environment.OSVersion.Platform != PlatformID.Win32NT) return Environment.OSVersion.Platform.ToString();

            if (Environment.OSVersion.Version.Major == 4 &&
                Environment.OSVersion.Version.Minor == 0)
            {
               return "Windows 95";
            }
            else if (Environment.OSVersion.Version.Major == 4 &&
                     Environment.OSVersion.Version.Minor == 10)
            {
               return "Windows 98";
            }
            else if (Environment.OSVersion.Version.Major == 5 &&
                     Environment.OSVersion.Version.Minor == 0)
            {
               return "Windows 2000";
            }
            else if (Environment.OSVersion.Version.Major == 5 &&
                     Environment.OSVersion.Version.Minor == 1)
            {
               return "Windows XP";
            }
            else if (Environment.OSVersion.Version.Major == 6 &&
                     Environment.OSVersion.Version.Minor == 0)
            {
               return "Vista";
            }
            else if (Environment.OSVersion.Version.Major == 6 &&
                     Environment.OSVersion.Version.Minor == 1)
            {
               return "Windows 7";
            }
            else
            {
               return "Windows";
            }
         }
      }

      public static string OSVersion
      {
         get
         {
            return Environment.OSVersion.Version.Major + "." + Environment.OSVersion.Version.Minor;
         }
      }

      public static string OSNameShort
      {
         get
         {
            if (Environment.OSVersion.Platform != PlatformID.Win32NT) return Environment.OSVersion.Platform.ToString();

            if (Environment.OSVersion.Version.Major == 4 &&
                Environment.OSVersion.Version.Minor == 0)
            {
               return "Win95";
            }
            else if (Environment.OSVersion.Version.Major == 4 &&
                     Environment.OSVersion.Version.Minor == 10)
            {
               return "Win98";
            }
            else if (Environment.OSVersion.Version.Major == 5 &&
                     Environment.OSVersion.Version.Minor == 0)
            {
               return "Win2k";
            }
            else if (Environment.OSVersion.Version.Major == 5 &&
                     Environment.OSVersion.Version.Minor == 1)
            {
               return "WinXP";
            }
            else if (Environment.OSVersion.Version.Major == 6 &&
                     Environment.OSVersion.Version.Minor == 0)
            {
               return "Vista";
            }
            else if (Environment.OSVersion.Version.Major == 6 &&
                     Environment.OSVersion.Version.Minor == 1)
            {
               return "Win7";
            }
            else
            {
               return "Win";
            }
         }
      }

      public static int IdleMinutes
      {
         get
         {
            uint idleTime = 0;
            Win32.LASTINPUTINFO lastInputInfo = new Win32.LASTINPUTINFO();
            lastInputInfo.cbSize = Marshal.SizeOf(lastInputInfo);
            lastInputInfo.dwTime = 0;

            if (Win32.GetLastInputInfo(ref lastInputInfo))
            {
               idleTime = (uint)Environment.TickCount - lastInputInfo.dwTime;
            }

            return (int)(idleTime / 60000);
         }
      }
   }
}